#pragma once
#include "export_dll.h"
#include "util_types.h"
#include <functional>
#include <cstdint>
#include <memory>

namespace yzrilyzr_lang{
	class String;
	class U_EXPORT_API BaseObject{
		public:
		constexpr BaseObject()=default;
		virtual ~BaseObject()=default;
		virtual int32_t hashCode()const{
			return static_cast<int32_t>(std::hash<const BaseObject *>{}(this));
		}
		virtual bool operator==(const BaseObject & other) const{
			return this == &other;
		}
		virtual bool operator!=(const BaseObject & other) const{
			return !(*this == other);
		}
		virtual String toString()const;
		friend bool operator==(const std::shared_ptr<BaseObject> & lhs,
							   const std::shared_ptr<BaseObject> & rhs){
			if(!lhs && !rhs) return true;
			if(!lhs || !rhs) return false;
			return *lhs == *rhs;
		}

		friend bool operator!=(const std::shared_ptr<BaseObject> & lhs,
							   const std::shared_ptr<BaseObject> & rhs){
			return !(lhs == rhs);
		}
	};
}

namespace std{
	template<>
	struct hash<yzrilyzr_lang::BaseObject>{
		size_t operator()(const yzrilyzr_lang::BaseObject & obj) const noexcept{
			return static_cast<size_t>(obj.hashCode());
		}
	};

	template<>
	struct hash<yzrilyzr_lang::BaseObject *>{
		size_t operator()(const yzrilyzr_lang::BaseObject * obj) const noexcept{
			return obj?static_cast<size_t>(obj->hashCode()):0;
		}
	};

	template<>
	struct hash<std::shared_ptr<yzrilyzr_lang::BaseObject>>{
		size_t operator()(const std::shared_ptr<yzrilyzr_lang::BaseObject> & obj) const noexcept{
			return obj?static_cast<size_t>(obj->hashCode()):0;
		}
	};
	template<>
	struct hash<std::unique_ptr<yzrilyzr_lang::BaseObject>>{
		size_t operator()(const std::unique_ptr<yzrilyzr_lang::BaseObject> & obj) const noexcept{
			return obj?static_cast<size_t>(obj->hashCode()):0;
		}
	};
	template<>
	struct equal_to<shared_ptr<yzrilyzr_lang::BaseObject>>{
		bool operator()(const shared_ptr<yzrilyzr_lang::BaseObject> & lhs,
						const shared_ptr<yzrilyzr_lang::BaseObject> & rhs) const{
			if(!lhs && !rhs) return true;
			if(!lhs || !rhs) return false;
			return *lhs == *rhs;
		}
	};

	template<>
	struct equal_to<unique_ptr<yzrilyzr_lang::BaseObject>>{
		bool operator()(const unique_ptr<yzrilyzr_lang::BaseObject> & lhs,
						const unique_ptr<yzrilyzr_lang::BaseObject> & rhs) const{
			if(!lhs && !rhs) return true;
			if(!lhs || !rhs) return false;
			return *lhs == *rhs;
		}
	};
}